//package io.iec.edp.caf.base.config.configclient;
//
//import io.iec.edp.caf.commons.configclient.bootstrap.BootstrapImportSelectorConfiguration;
//import org.springframework.beans.factory.ListableBeanFactory;
//import org.springframework.boot.Banner;
//import org.springframework.boot.SpringApplication;
//import org.springframework.boot.WebApplicationType;
//import org.springframework.boot.builder.ParentContextApplicationContextInitializer;
//import org.springframework.boot.builder.SpringApplicationBuilder;
//import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
//import org.springframework.boot.context.event.ApplicationFailedEvent;
//import org.springframework.boot.env.OriginTrackedMapPropertySource;
//import org.springframework.context.ApplicationContextInitializer;
//import org.springframework.context.ApplicationEvent;
//import org.springframework.context.ApplicationListener;
//import org.springframework.context.ConfigurableApplicationContext;
//import org.springframework.context.event.SmartApplicationListener;
//import org.springframework.core.Ordered;
//import org.springframework.core.annotation.AnnotationAwareOrderComparator;
//import org.springframework.core.annotation.Order;
//import org.springframework.core.env.*;
//import org.springframework.util.ReflectionUtils;
//import org.springframework.util.StringUtils;
//
//import java.lang.reflect.Field;
//import java.util.*;
//
///**
// * 參考于spring cloud context的bootstrapapplicationListner
// * 在spring boot启动过程中会经历如下3个阶段
// * (1)prepareEnvironment【触发ApplicationListner】
// * (2)prepareContext【触发ApplicationContextInitializer接口】
// * (3)refreshContext【创建bean】
// *
// * boot启动后
// * （1）prepareEviroment的阶段，会扫描到所有的ApplicationListner接口触发，这里通过这个接口可以创建一个cloud的application run起来。
// * （2）cloud的application run起来后，会将cloud里的ApplicationContextInitializer接口注册到boot的application里。然后通过
// *     refreshContext初始化bean然后再返回到boot的run里
// *  (3) 回到boot的run里后，会到prepareContext,这时触发cloud里的ApplicationContextInitializer接口（接口里需要2中的bean）取配置中心拉取配置
// *  (4) 拉取配置完毕后再回到boot继续启动
// */
//public class CAFBootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
//
//    public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
//
//    /**
//     * The default order for this listener.
//     */
//    public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
//
//    /**
//     * The name of the default properties.
//     */
//    public static final String DEFAULT_PROPERTIES = "defaultProperties";
//
//    private int order = DEFAULT_ORDER;
//
//    @Override
//    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
//        ConfigurableEnvironment environment = event.getEnvironment();
//        if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,true)) {
//            return;
//        }
//        // don't listen to events in a bootstrap context
//        // 通过bootstrap启动的事件，不在重复执行改操作，直接return
//        if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
//            return;
//        }
//
//        ConfigurableApplicationContext context = null;
//        String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
//        for (ApplicationContextInitializer<?> initializer : event.getSpringApplication().getInitializers()) {
//            if (initializer instanceof ParentContextApplicationContextInitializer) {
//                context = findBootstrapContext((ParentContextApplicationContextInitializer) initializer, configName);
//            }
//        }
//        if (context == null) {
//            context = bootstrapServiceContext(environment, event.getSpringApplication(), configName);
//            event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context));
//        }
//
//        apply(context, event.getSpringApplication(), environment);
//    }
//
//    @Override
//    public int getOrder() {
//        return 0;
//    }
//
//    //cloud的简化逻辑
//    private ConfigurableApplicationContext bootstrapServiceContext(
//            ConfigurableEnvironment environment,
//            final SpringApplication application,
//            String configName) {
//        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
//        MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
//        for (PropertySource<?> source : bootstrapProperties) {
//            bootstrapProperties.remove(source.getName());
//        }
//        String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
//        Map<String, Object> bootstrapMap = new HashMap<>();
//        bootstrapMap.put("spring.config.name", configName);
//        // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
//        // will fail
//        // force the environment to use none, because if though it is set below in the
//        // builder
//        // the environment overrides it
//        bootstrapMap.put("spring.main.web-application-type", "none");
//        if (StringUtils.hasText(configLocation)) {
//            bootstrapMap.put("spring.config.location", configLocation);
//        }
//        bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
//        for (PropertySource<?> source : environment.getPropertySources()) {
//            if (source instanceof PropertySource.StubPropertySource) {
//                continue;
//            }
//            bootstrapProperties.addLast(source);
//        }
//        // TODO: is it possible or sensible to share a ResourceLoader?
//        SpringApplicationBuilder builder = new SpringApplicationBuilder()
//                .profiles(environment.getActiveProfiles()).bannerMode(Banner.Mode.OFF)
//                .environment(bootstrapEnvironment)
//                // Don't use the default properties in this builder
//                .registerShutdownHook(false).logStartupInfo(false)
//                .web(WebApplicationType.NONE);
//        final SpringApplication builderApplication = builder.application();
//        if (builderApplication.getMainApplicationClass() == null) {
//            // gh_425:
//            // SpringApplication cannot deduce the MainApplicationClass here
//            // if it is booted from SpringBootServletInitializer due to the
//            // absense of the "main" method in stackTraces.
//            // But luckily this method's second parameter "application" here
//            // carries the real MainApplicationClass which has been explicitly
//            // set by SpringBootServletInitializer itself already.
//            builder.main(application.getMainApplicationClass());
//        }
////        BootstrapImportSelectorConfiguration
////        builder.sources(ConfigServiceAutoConfiguration.class);
//        builder.sources(BootstrapImportSelectorConfiguration.class);
//        final ConfigurableApplicationContext context = builder.run();
//        // gh-214 using spring.application.name=bootstrap to set the context id via
//        // `ContextIdApplicationContextInitializer` prevents apps from getting the actual
//        // spring.application.name
//        // during the bootstrap phase.
//        context.setId("bootstrap");
//        // Make the bootstrap context a parent of the app context
//        addAncestorInitializer(application, context);
//        // It only has properties in it now that we don't want in the parent so remove
//        // it (and it will be added back later)
//        bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
//        mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
//        return context;
//    }
//
//    //cloud里的原始逻辑
////    private ConfigurableApplicationContext bootstrapServiceContext(
////            ConfigurableEnvironment environment,
////            final SpringApplication application,
////            String configName) {
////        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
////        MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
////        for (PropertySource<?> source : bootstrapProperties) {
////            bootstrapProperties.remove(source.getName());
////        }
////        String configLocation = environment.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
////        Map<String, Object> bootstrapMap = new HashMap<>();
////        bootstrapMap.put("spring.config.name", configName);
////        // if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
////        // will fail
////        // force the environment to use none, because if though it is set below in the
////        // builder
////        // the environment overrides it
////        bootstrapMap.put("spring.main.web-application-type", "none");
////        if (StringUtils.hasText(configLocation)) {
////            bootstrapMap.put("spring.config.location", configLocation);
////        }
////        bootstrapProperties.addFirst(new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
////        for (PropertySource<?> source : environment.getPropertySources()) {
////            if (source instanceof PropertySource.StubPropertySource) {
////                continue;
////            }
////            bootstrapProperties.addLast(source);
////        }
////        // TODO: is it possible or sensible to share a ResourceLoader?
////        SpringApplicationBuilder builder = new SpringApplicationBuilder()
////                .profiles(environment.getActiveProfiles()).bannerMode(Banner.Mode.OFF)
////                .environment(bootstrapEnvironment)
////                // Don't use the default properties in this builder
////                .registerShutdownHook(false).logStartupInfo(false)
////                .web(WebApplicationType.NONE);
////        final SpringApplication builderApplication = builder.application();
////        if (builderApplication.getMainApplicationClass() == null) {
////            // gh_425:
////            // SpringApplication cannot deduce the MainApplicationClass here
////            // if it is booted from SpringBootServletInitializer due to the
////            // absense of the "main" method in stackTraces.
////            // But luckily this method's second parameter "application" here
////            // carries the real MainApplicationClass which has been explicitly
////            // set by SpringBootServletInitializer itself already.
////            builder.main(application.getMainApplicationClass());
////        }
////        if (environment.getPropertySources().contains("refreshArgs")) {
////            // If we are doing a context refresh, really we only want to refresh the
////            // Environment, and there are some toxic listeners (like the
////            // LoggingApplicationListener) that affect global static state, so we need a
////            // way to switch those off.
////            builderApplication.setListeners(filterListeners(builderApplication.getListeners()));
////        }
////        builder.sources(BootstrapImportSelectorConfiguration.class);
////        final ConfigurableApplicationContext context = builder.run();
////        // gh-214 using spring.application.name=bootstrap to set the context id via
////        // `ContextIdApplicationContextInitializer` prevents apps from getting the actual
////        // spring.application.name
////        // during the bootstrap phase.
////        context.setId("bootstrap");
////        // Make the bootstrap context a parent of the app context
////        addAncestorInitializer(application, context);
////        // It only has properties in it now that we don't want in the parent so remove
////        // it (and it will be added back later)
////        bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
////        mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
////        return context;
////    }
//
//    private void apply(ConfigurableApplicationContext context,
//                       SpringApplication application, ConfigurableEnvironment environment) {
//        @SuppressWarnings("rawtypes")
//        List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context,ApplicationContextInitializer.class);
//        application.addInitializers(initializers.toArray(new ApplicationContextInitializer[initializers.size()]));
////        addBootstrapDecryptInitializer(application);
//    }
//
//    //跟加解密配置文件相关
////    private void addBootstrapDecryptInitializer(SpringApplication application) {
////        DelegatingEnvironmentDecryptApplicationInitializer decrypter = null;
////        for (ApplicationContextInitializer<?> ini : application.getInitializers()) {
////            if (ini instanceof EnvironmentDecryptApplicationInitializer) {
////                @SuppressWarnings("unchecked")
////                ApplicationContextInitializer del = (ApplicationContextInitializer) ini;
////                decrypter = new DelegatingEnvironmentDecryptApplicationInitializer(del);
////            }
////        }
////        if (decrypter != null) {
////            application.addInitializers(decrypter);
////        }
////    }
//
//
//    private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context,
//                                              Class<T> type) {
//        List<T> result = new ArrayList<T>();
//        for (String name : context.getBeanNamesForType(type)) {
//            result.add(context.getBean(name, type));
//        }
//        AnnotationAwareOrderComparator.sort(result);
//        return result;
//    }
//
//    private ConfigurableApplicationContext findBootstrapContext(
//            ParentContextApplicationContextInitializer initializer, String configName) {
//        Field field = ReflectionUtils
//                .findField(ParentContextApplicationContextInitializer.class, "parent");
//        ReflectionUtils.makeAccessible(field);
//        ConfigurableApplicationContext parent = safeCast(
//                ConfigurableApplicationContext.class,
//                ReflectionUtils.getField(field, initializer));
//        if (parent != null && !configName.equals(parent.getId())) {
//            parent = safeCast(ConfigurableApplicationContext.class, parent.getParent());
//        }
//        return parent;
//    }
//
//    private <T> T safeCast(Class<T> type, Object object) {
//        try {
//            return type.cast(object);
//        }
//        catch (ClassCastException e) {
//            return null;
//        }
//    }
//
//    private void addAncestorInitializer(SpringApplication application,
//                                        ConfigurableApplicationContext context) {
//        boolean installed = false;
//        for (ApplicationContextInitializer<?> initializer : application
//                .getInitializers()) {
//            if (initializer instanceof AncestorInitializer) {
//                installed = true;
//                // New parent
//                ((AncestorInitializer) initializer).setParent(context);
//            }
//        }
//        if (!installed) {
//            application.addInitializers(new AncestorInitializer(context));
//        }
//
//    }
//
//    private void mergeDefaultProperties(MutablePropertySources environment,
//                                        MutablePropertySources bootstrap) {
//        String name = DEFAULT_PROPERTIES;
//        if (bootstrap.contains(name)) {
//            PropertySource<?> source = bootstrap.get(name);
//            if (!environment.contains(name)) {
//                environment.addLast(source);
//            }
//            else {
//                PropertySource<?> target = environment.get(name);
//                if (target instanceof MapPropertySource && target != source
//                        && source instanceof MapPropertySource) {
//                    Map<String, Object> targetMap = ((MapPropertySource) target)
//                            .getSource();
//                    Map<String, Object> map = ((MapPropertySource) source).getSource();
//                    for (String key : map.keySet()) {
//                        if (!target.containsProperty(key)) {
//                            targetMap.put(key, map.get(key));
//                        }
//                    }
//                }
//            }
//        }
//        mergeAdditionalPropertySources(environment, bootstrap);
//    }
//
//    private void mergeAdditionalPropertySources(MutablePropertySources environment,
//                                                MutablePropertySources bootstrap) {
//        PropertySource<?> defaultProperties = environment.get(DEFAULT_PROPERTIES);
//        ExtendedDefaultPropertySource result = defaultProperties instanceof ExtendedDefaultPropertySource
//                ? (ExtendedDefaultPropertySource) defaultProperties
//                : new ExtendedDefaultPropertySource(DEFAULT_PROPERTIES,
//                defaultProperties);
//        for (PropertySource<?> source : bootstrap) {
//            if (!environment.contains(source.getName())) {
//                result.add(source);
//            }
//        }
//        for (String name : result.getPropertySourceNames()) {
//            bootstrap.remove(name);
//        }
//        addOrReplace(environment, result);
//        addOrReplace(bootstrap, result);
//    }
//
//    private void addOrReplace(MutablePropertySources environment,
//                              PropertySource<?> result) {
//        if (environment.contains(result.getName())) {
//            environment.replace(result.getName(), result);
//        }
//        else {
//            environment.addLast(result);
//        }
//    }
//
//    private static class ExtendedDefaultPropertySource
//            extends SystemEnvironmentPropertySource {
//
//        private final CompositePropertySource sources;
//
//        private final List<String> names = new ArrayList<>();
//
//        ExtendedDefaultPropertySource(String name, PropertySource<?> propertySource) {
//            super(name, findMap(propertySource));
//            this.sources = new CompositePropertySource(name);
//        }
//
//        @SuppressWarnings("unchecked")
//        private static Map<String, Object> findMap(PropertySource<?> propertySource) {
//            if (propertySource instanceof MapPropertySource) {
//                return (Map<String, Object>) propertySource.getSource();
//            }
//            return new LinkedHashMap<String, Object>();
//        }
//
//        public CompositePropertySource getPropertySources() {
//            return this.sources;
//        }
//
//        public List<String> getPropertySourceNames() {
//            return this.names;
//        }
//
//        public void add(PropertySource<?> source) {
//            // Only add map property sources added by boot, see gh-476
//            if (source instanceof OriginTrackedMapPropertySource
//                    && !this.names.contains(source.getName())) {
//                this.sources.addPropertySource(source);
//                this.names.add(source.getName());
//            }
//        }
//
//        @Override
//        public Object getProperty(String name) {
//            if (this.sources.containsProperty(name)) {
//                return this.sources.getProperty(name);
//            }
//            return super.getProperty(name);
//        }
//
//        @Override
//        public boolean containsProperty(String name) {
//            if (this.sources.containsProperty(name)) {
//                return true;
//            }
//            return super.containsProperty(name);
//        }
//
//        @Override
//        public String[] getPropertyNames() {
//            List<String> names = new ArrayList<>();
//            names.addAll(Arrays.asList(this.sources.getPropertyNames()));
//            names.addAll(Arrays.asList(super.getPropertyNames()));
//            return names.toArray(new String[0]);
//        }
//
//    }
//
//
//    private static class AncestorInitializer implements
//            ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
//
//        private ConfigurableApplicationContext parent;
//
//        AncestorInitializer(ConfigurableApplicationContext parent) {
//            this.parent = parent;
//        }
//
//        public void setParent(ConfigurableApplicationContext parent) {
//            this.parent = parent;
//        }
//
//        @Override
//        public int getOrder() {
//            // Need to run not too late (so not unordered), so that, for instance, the
//            // ContextIdApplicationContextInitializer runs later and picks up the merged
//            // Environment. Also needs to be quite early so that other initializers can
//            // pick up the parent (especially the Environment).
//            return Ordered.HIGHEST_PRECEDENCE + 5;
//        }
//
//        @Override
//        public void initialize(ConfigurableApplicationContext context) {
//            while (context.getParent() != null && context.getParent() != context) {
//                context = (ConfigurableApplicationContext) context.getParent();
//            }
//            reorderSources(context.getEnvironment());
//            new ParentContextApplicationContextInitializer(this.parent)
//                    .initialize(context);
//        }
//
//        private void reorderSources(ConfigurableEnvironment environment) {
//            PropertySource<?> removed = environment.getPropertySources()
//                    .remove(DEFAULT_PROPERTIES);
//            if (removed instanceof ExtendedDefaultPropertySource) {
//                ExtendedDefaultPropertySource defaultProperties = (ExtendedDefaultPropertySource) removed;
//                environment.getPropertySources().addLast(new MapPropertySource(
//                        DEFAULT_PROPERTIES, defaultProperties.getSource()));
//                for (PropertySource<?> source : defaultProperties.getPropertySources()
//                        .getPropertySources()) {
//                    if (!environment.getPropertySources().contains(source.getName())) {
//                        environment.getPropertySources().addBefore(DEFAULT_PROPERTIES,
//                                source);
//                    }
//                }
//            }
//        }
//
//    }
//
//
//
//    private static class CloseContextOnFailureApplicationListener
//            implements SmartApplicationListener {
//
//        private final ConfigurableApplicationContext context;
//
//        CloseContextOnFailureApplicationListener(ConfigurableApplicationContext context) {
//            this.context = context;
//        }
//
//        @Override
//        public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
//            return ApplicationFailedEvent.class.isAssignableFrom(eventType);
//        }
//
//        @Override
//        public void onApplicationEvent(ApplicationEvent event) {
//            if (event instanceof ApplicationFailedEvent) {
//                this.context.close();
//            }
//
//        }
//
//    }
//
//
//    @Order(Ordered.HIGHEST_PRECEDENCE + 9)
//    private static class DelegatingEnvironmentDecryptApplicationInitializer
//            implements ApplicationContextInitializer<ConfigurableApplicationContext> {
//
//        private ApplicationContextInitializer<ConfigurableApplicationContext> delegate;
//
//        DelegatingEnvironmentDecryptApplicationInitializer(
//                ApplicationContextInitializer<ConfigurableApplicationContext> delegate) {
//            this.delegate = delegate;
//        }
//
//        @Override
//        public void initialize(ConfigurableApplicationContext applicationContext) {
//            this.delegate.initialize(applicationContext);
//        }
//
//    }
//}
